package com.siashan.unipay4j.wx.transfer;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.siashan.unipay4j.core.IPayConfig;
import com.siashan.unipay4j.core.PayChannel;
import com.siashan.unipay4j.core.Result;
import com.siashan.unipay4j.core.exception.Unipay4jException;
import com.siashan.unipay4j.core.exception.Unipay4jOperationNotSupportedException;
import com.siashan.unipay4j.core.transfer.*;
import com.siashan.unipay4j.core.util.EnumUtil;
import com.siashan.unipay4j.core.util.MoneyUtil;
import com.siashan.unipay4j.core.util.RequestUtil;
import com.siashan.unipay4j.core.util.StrUtil;
import com.siashan.unipay4j.wx.AbstractWxPay;
import com.siashan.unipay4j.wx.WxPayVersionEnum;
import com.siashan.unipay4j.wx.model.QueryOrderResult;
import com.siashan.unipay4j.wx.model.transfer.WxTransferDetail;
import com.siashan.unipay4j.wx.model.transfer.WxTransferModel;
import com.siashan.unipay4j.wx.model.transfer.newmodel.*;
import com.siashan.unipay4j.wx.sdk.WXPayConstants;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.constant.WechatPayHttpHeaders;
import com.wechat.pay.contrib.apache.httpclient.notification.Notification;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationHandler;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationRequest;
import com.wechat.pay.contrib.apache.httpclient.util.RsaCryptoUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.StatusLine;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;

import javax.crypto.IllegalBlockSizeException;
import javax.servlet.http.HttpServletRequest;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.apache.http.HttpStatus.SC_MULTIPLE_CHOICES;
import static org.apache.http.HttpStatus.SC_OK;

/**
 * 微信转账
 *
 * @author siashan
 * @since v1.0.1
 **/
@Slf4j
public class WxPayTransferNew extends AbstractWxPay {


    public WxPayTransferNew() {
    }

    public WxPayTransferNew(IPayConfig payConfig) {
        super(payConfig);
    }

    /**
     * 商家转账
     *
     * @param model 转账参数
     * @return 转账结果
     * @apiNote 商家转账到微信用户零钱，新逻辑，需用户手动确认收款
     */
    public Result<WxTransferNewResponse> transfer(WxTransferNewModel model) {
        log.info("WxTransfer::=>商家转账单号{} 转账请求到达", model.getOutBillNo());
        WechatPayHttpClientBuilder builder = loadBuilder();
        model.setAppid(this.wxPayConfig.getAppId());
        if (StrUtil.isNotEmpty(model.getUserName())){
            PublicKey publicKey = loadVerifier().getValidPublicKey();
            try {
                model.setUserName(RsaCryptoUtil.encryptOAEP(model.getUserName(),publicKey));
            } catch (IllegalBlockSizeException e) {
                throw new Unipay4jException("敏感信息【真实姓名】加密异常",e);
            }
        }
        model.check();
        // ... 接下来，你仍然可以通过builder设置各种参数，来配置你的HttpClient
        // 通过WechatPayHttpClientBuilder构造的HttpClient，会自动的处理签名和验签
        CloseableHttpClient httpClient = builder.build();
        HttpPost httpPost = initHttpPost(WXPayConstants.V3_PAY_URL_TRANSFER_BillS, JSON.toJSONString(model));
        try {
            CloseableHttpResponse response = httpClient.execute(httpPost);
            StatusLine statusLine = response.getStatusLine();
            String body = EntityUtils.toString(response.getEntity());
            log.info("WxTransfer::=>商家转账单号{} 转账返回报文:{}", model.getOutBillNo(), body);
            int statusCode = statusLine.getStatusCode();
            if (statusCode >= SC_OK && statusCode < SC_MULTIPLE_CHOICES) {
                WxTransferNewResponse resp = JSON.parseObject(body,WxTransferNewResponse.class);
                log.info("WxTransfer::=>商家转账单号{} 受理  成功", model.getOutBillNo());
                return Result.tradeSuccess(resp);
            } else {
                JSONObject bodyJson = JSON.parseObject(body);
                String errCode = bodyJson.getString(WXPayConstants.CODE);
                String errCodeDes = bodyJson.getString(WXPayConstants.MESSAGE);
                log.info("WxTransfer::=>商家转账单号{} 转账失败,错误代码{},错误原因{}", model.getOutBillNo(), errCode, errCodeDes);
                return Result.tradeFail(errCode, errCodeDes);
            }
        } catch (Exception e) {
            log.warn("WxTransfer=>商家转账单号{} 转账发生异常", model.getOutBillNo(), e);
        }
        return Result.exception();
    }

    /**
     * 撤销转账
     *
     * @param outBillNo 商家单号
     * @return 转账结果
     */
    public Result<WxTransferNewCancelResponse> cancel(String outBillNo) {
        log.info("WxTransfer::=>撤销转账,单号{} 请求到达", outBillNo);
        WechatPayHttpClientBuilder builder = loadBuilder();
        // ... 接下来，你仍然可以通过builder设置各种参数，来配置你的HttpClient
        // 通过WechatPayHttpClientBuilder构造的HttpClient，会自动的处理签名和验签
        CloseableHttpClient httpClient = builder.build();
        HttpPost httpPost = initHttpPost(wrapOutBillNoCancelUrl(outBillNo));
        try {
            CloseableHttpResponse response = httpClient.execute(httpPost);
            StatusLine statusLine = response.getStatusLine();
            String body = EntityUtils.toString(response.getEntity());
            log.info("WxTransfer::=>撤销转账,单号{} 返回报文:{}", outBillNo, body);
            int statusCode = statusLine.getStatusCode();
            if (statusCode >= SC_OK && statusCode < SC_MULTIPLE_CHOICES) {
                WxTransferNewCancelResponse resp = JSON.parseObject(body,WxTransferNewCancelResponse.class);
                log.info("WxTransfer::=>撤销转账,单号{} 受理  成功", outBillNo);
                return Result.tradeSuccess(resp);
            } else {
                JSONObject bodyJson = JSON.parseObject(body);
                String errCode = bodyJson.getString(WXPayConstants.CODE);
                String errCodeDes = bodyJson.getString(WXPayConstants.MESSAGE);
                log.info("WxTransfer::=>撤销转账,单号{} 转账失败,错误代码{},错误原因{}", outBillNo, errCode, errCodeDes);
                return Result.tradeFail(errCode, errCodeDes);
            }
        } catch (Exception e) {
            log.warn("WxTransfer=>撤销转账,单号{} 发生异常", outBillNo, e);
        }
        return Result.exception();
    }


    /**
     * 商户单号查询转账单
     *
     * @param outBillNo 商家单号
     * @return 转账结果
     */
    public Result<WxTransferNewQueryResponse> queryByOutBillNo(String outBillNo) {
        log.info("WxTransfer::=>查询转账单,单号{} 请求到达", outBillNo);
        WechatPayHttpClientBuilder builder = loadBuilder();
        // ... 接下来，你仍然可以通过builder设置各种参数，来配置你的HttpClient
        // 通过WechatPayHttpClientBuilder构造的HttpClient，会自动的处理签名和验签
        CloseableHttpClient httpClient = builder.build();
        HttpGet httpGet = initOutBillNoQuery(outBillNo);
        try {
            CloseableHttpResponse response = httpClient.execute(httpGet);
            StatusLine statusLine = response.getStatusLine();
            String body = EntityUtils.toString(response.getEntity());
            log.info("WxTransfer::=>查询转账单,单号{} 返回报文:{}", outBillNo, body);
            int statusCode = statusLine.getStatusCode();
            if (statusCode >= SC_OK && statusCode < SC_MULTIPLE_CHOICES) {
                WxTransferNewQueryResponse resp = JSON.parseObject(body,WxTransferNewQueryResponse.class);
                log.info("WxTransfer::=>查询转账单,单号{} 受理  成功", outBillNo);
                return Result.tradeSuccess(resp);
            } else {
                JSONObject bodyJson = JSON.parseObject(body);
                String errCode = bodyJson.getString(WXPayConstants.CODE);
                String errCodeDes = bodyJson.getString(WXPayConstants.MESSAGE);
                log.info("WxTransfer::=>查询转账单,单号{} 转账失败,错误代码{},错误原因{}", outBillNo, errCode, errCodeDes);
                return Result.tradeFail(errCode, errCodeDes);
            }
        } catch (Exception e) {
            log.warn("WxTransfer=>查询转账单,单号{} 发生异常", outBillNo, e);
        }
        return Result.exception();
    }


    /**
     * 微信单号查询转账单
     *
     * @param transferBillNo 微信单号
     * @return 转账结果
     */
    public Result<WxTransferNewQueryResponse> queryByTransferBillNo(String transferBillNo) {
        log.info("WxTransfer::=>查询转账单,单号{} 请求到达", transferBillNo);
        WechatPayHttpClientBuilder builder = loadBuilder();
        // ... 接下来，你仍然可以通过builder设置各种参数，来配置你的HttpClient
        // 通过WechatPayHttpClientBuilder构造的HttpClient，会自动的处理签名和验签
        CloseableHttpClient httpClient = builder.build();
        HttpGet httpGet = initTransferBillNoQuery(transferBillNo);
        try {
            CloseableHttpResponse response = httpClient.execute(httpGet);
            StatusLine statusLine = response.getStatusLine();
            String body = EntityUtils.toString(response.getEntity());
            log.info("WxTransfer::=>查询转账单,单号{} 返回报文:{}", transferBillNo, body);
            int statusCode = statusLine.getStatusCode();
            if (statusCode >= SC_OK && statusCode < SC_MULTIPLE_CHOICES) {
                WxTransferNewQueryResponse resp = JSON.parseObject(body,WxTransferNewQueryResponse.class);
                log.info("WxTransfer::=>查询转账单,单号{} 受理  成功", transferBillNo);
                return Result.tradeSuccess(resp);
            } else {
                JSONObject bodyJson = JSON.parseObject(body);
                String errCode = bodyJson.getString(WXPayConstants.CODE);
                String errCodeDes = bodyJson.getString(WXPayConstants.MESSAGE);
                log.info("WxTransfer::=>查询转账单,单号{} 转账失败,错误代码{},错误原因{}", transferBillNo, errCode, errCodeDes);
                return Result.tradeFail(errCode, errCodeDes);
            }
        } catch (Exception e) {
            log.warn("WxTransfer=>查询转账单,单号{} 发生异常", transferBillNo, e);
        }
        return Result.exception();
    }


    /**
     * 支付回调 V3
     *
     * @param request Request请求
     *
     * @return 回调处理结果
     */
    public Result<WxTransferNewNotifyResponse> notify(HttpServletRequest request) {

        try {
            // 请求头Wechatpay-Nonce
            String nonce = request.getHeader(WechatPayHttpHeaders.WECHAT_PAY_NONCE);
            // 请求头Wechatpay-Timestamp
            String timestamp = request.getHeader(WechatPayHttpHeaders.WECHAT_PAY_TIMESTAMP);
            // 请求头Wechatpay-Signature
            String signature = request.getHeader(WechatPayHttpHeaders.WECHAT_PAY_SIGNATURE);
            // 此处一定要使用微信回调返回的序列号，不能使用商户私有序列号，不然会验签失败
            String serialNumber =request.getHeader(WechatPayHttpHeaders.WECHAT_PAY_SERIAL);
            String body = RequestUtil.getStrFromInputSteam(request.getInputStream());
            // 构建request，传入必要参数
            NotificationRequest notificationRequest = new NotificationRequest.Builder().withSerialNumber(serialNumber)
                    .withNonce(nonce)
                    .withTimestamp(timestamp)
                    .withSignature(signature)
                    .withBody(body)
                    .build();
            NotificationHandler handler = new NotificationHandler(loadVerifier(), wxPayConfig.getV3Key().getBytes(StandardCharsets.UTF_8));
            // 验签和解析请求体
            Notification notification = handler.parse(notificationRequest);
            log.info("WxTransfer::=> 回调通知报文：{}",notification.toString());
            String decryptData = notification.getDecryptData();
            log.info("WxTransfer::=> 回调通知业务明文：{}",decryptData);
            WxTransferNewNotifyResponse orderResult = JSON.parseObject(decryptData, WxTransferNewNotifyResponse.class);
            return  Result.tradeSuccess(orderResult);
        } catch (Exception e) {
            log.warn("WxTransfer::=> 回调时发生异常", e);
        }
        return Result.exception();
    }




    /**
     * 回调成功，同步返回给支付平台的报文
     *
     * @apiNote V3 HTTP应答状态码需返回200或204，无需返回应答报文
     *
     * @return 返回报文
     */
    public String successMsg() {
        String msg  = "{ \"code\": \"SUCCESS\",\"message\": \"SUCCESS\"}";
        return msg;
    }

    /**
     * 回调失败，同步返回给支付平台的报文
     *
     * @apiNote V3 HTTP应答状态码需返回5XX或4XX，同时需返回应答报文
     *
     * @return 返回报文
     */
    public String failMsg() {
        String msg  = "{ \"code\": \"FAIL\",\"message\": \"FAILED\"}";
        return msg;
    }


    //******************************************   private method ***************************************************//
    private String wrapOutBillNoCancelUrl(String outBillNo) {
        return WXPayConstants.V3_PAY_URL_TRANSFER_CANCEL.replace("{out_bill_no}", outBillNo.trim());
    }



    private HttpGet initOutBillNoQuery(String outBillNo) {
        String url = WXPayConstants.V3_PAY_URL_TRANSFER_QUERY_OUT_BILL_NO.replace("{out_bill_no}", outBillNo.trim());
        return initHttpGet(url);
    }

    private HttpGet initTransferBillNoQuery(String transferBillNo) {
        String url = WXPayConstants.V3_PAY_URL_TRANSFER_QUERY_TRANS_BILL_NO.replace("{transfer_bill_no}", transferBillNo.trim());
        return initHttpGet(url);
    }




    private HttpGet initHttpGet(String url) {

        try {
            URIBuilder uriBuilder = new URIBuilder(url);
            HttpGet httpGet = new HttpGet(uriBuilder.build());
            httpGet.addHeader("Accept", "application/json");
            httpGet.addHeader("Content-type", "application/json; charset=utf-8");
            return httpGet;
        } catch (URISyntaxException e) {
            log.warn("构建转账查询请求Url异常", e);
            throw new RuntimeException();
        }
    }
}
